package com.cloudlead.web.resource;

import com.cloudlead.common.lang.Messages;
import com.cloudlead.common.lang.Module;
import com.cloudlead.common.lang.persistence.Tree;
import com.cloudlead.common.worker.ActionHistory;
import com.cloudlead.common.worker.ApiHistoryService;
import com.cloudlead.common.worker.ApiRegistry;
import com.cloudlead.common.worker.ApiService;
import com.cloudlead.common.worker.action.ActionNotFoundException;
import com.cloudlead.common.worker.desc.ActionDesc;
import com.cloudlead.common.worker.desc.ModuleDesc;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Controller
@Path(CloudleadApiResources.API_RESOURCE)
@Produces(MediaType.APPLICATION_JSON)
public class CloudleadApiResources {

    static final String API_RESOURCE = "request";

    @Inject
    ApiService apiService;
    @Inject
    ApiRegistry apiRegistry;
    @Inject
    ApiHistoryService apiHistoryService;

    @Context
    HttpServletRequest request;

    @Context
    HttpServletResponse response;

    /**
     * 获取API列表
     *
     * @return
     */
    @GET
    public Collection<ActionDesc> getApiList() {
        Collection<ActionDesc> values = apiRegistry.getAllApiDesc().values();
        return values;
    }

    /**
     * 获取API模块列表
     *
     * @return
     */
    @GET
    @Path("/modules")
    public Collection<Tree> getApiModuleList() {
        Map<ModuleDesc, Tree> allModules = new HashMap<>();
        List<ModuleDesc> allRemoveModules = new ArrayList<>();
        for (ModuleDesc module : apiRegistry.getAllModuleDesc().values()) {
            Tree tree = allModules.get(module);
            if (null == tree) {
                tree = getTree(module);
                allModules.put(module, tree);
            }
            findParent(module, tree, allModules, allRemoveModules);
        }
        allRemoveModules.forEach(allModules::remove);
        return allModules.values();
    }


    private void findParent(ModuleDesc module, Tree tree, Map<ModuleDesc, Tree> allModules, List<ModuleDesc> allRemoveModules) {
        ModuleDesc parent = module.getParent();
        if (null != parent) {
            Tree parentTree = allModules.get(parent);
            if (null == parentTree) {
                parentTree = getTree(parent);
                allModules.put(parent, parentTree);
            }
            List<Tree> children = parentTree.getChildren();
            if (null == children) {
                children = new ArrayList<>();
                parentTree.setChildren(children);
            }
            if (!children.contains(tree)) {
                children.add(tree);
            }
            allRemoveModules.add(module);
            findParent(parent, parentTree, allModules, allRemoveModules);
        }
    }

    private Tree getTree(ModuleDesc module) {
        Tree tree = new Tree();
        tree.setId(module.getFullName());
        tree.setText(StringUtils.isBlank(module.getComment()) ? module.getFullName() : module.getComment());
        tree.setState("closed");
        return tree;
    }

    /**
     * 获取action列表
     *
     * @return
     */
    @GET
    @Path("/actions")
    public Collection<Tree> getActionList(@QueryParam("id") String id) throws Exception {
        if (StringUtils.isNotBlank(id)) {
            ModuleDesc moduleDesc = getModuleDesc(id);
            if (null != moduleDesc) {
                Tree tree = getTree(moduleDesc);
                findChildWithActions(tree.getChildren(), tree);
                return tree.getChildren();
            }
            return null;
        } else {
            Collection<Tree> trees = getApiModuleList();
            for (Tree tree : trees) {
                findChildWithActions(tree.getChildren(), tree);
            }
            return trees;
        }
    }

    private void findChildWithActions(List<Tree> children, Tree tree) {
        if (null != children) {
            for (Tree childTree : children) {
                findChildWithActions(childTree.getChildren(), childTree);
            }
        }
        Map<String, ActionDesc> apis = apiRegistry.getApiDescs(tree.getId());
        if (null != apis) {
            if (null == children) {
                children = new ArrayList<>();
                tree.setChildren(children);
            }
            for (ActionDesc actionDesc : apis.values()) {
                Tree actionTree = new Tree();
                actionTree.setId(actionDesc.getName());
                actionTree.setText(StringUtils.isBlank(actionDesc.getComment()) ? actionDesc.getName() : actionDesc.getComment());
                children.add(actionTree);
            }
        }
    }


    /**
     * 获取模块定义
     *
     * @return
     * @throws Exception
     */
    @GET
    @Path("/modules/{module}")
    public ModuleDesc getModuleDesc(@PathParam("module") String module) throws Exception {
        ModuleDesc moduleDesc = apiRegistry.getAllModuleDesc().get(module);
        return moduleDesc;
    }

    /**
     * 获取模块定义
     *
     * @return
     * @throws Exception
     */
    @GET
    @Path("/modules/{module}/enabled")
    public boolean isModuleEnabled(@PathParam("module") String moduleName) throws Exception {
        Module module = apiRegistry.getAllModule().get(moduleName);
        if (null != module) {
            return module.isEnabled();
        }
        return false;
    }

    /**
     * 启用模块
     */
    @PUT
    @Path("/modules/{module}/enable")
    public void enableModule(@PathParam("module") String module) {
        apiRegistry.enableModule(module);
    }

    /**
     * 禁用模块
     */
    @PUT
    @Path("/modules/{module}/disable")
    public void disableModule(@PathParam("module") String module) {
        apiRegistry.disableModule(module);
    }

    /**
     * 获取API
     *
     * @return
     * @throws Exception
     */
    @GET
    @Path("/{action}")
    public ActionDesc getApiDesc(@PathParam("action") String action) throws Exception {
        ActionDesc apiInfo = apiRegistry.getAllApiDesc().get(action);
        return apiInfo;
    }

    @GET
    @Path("/history")
    public Collection<ActionHistory> getAllHistory() throws Exception {
        return apiHistoryService.loadAll();
    }

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");

    /**
     * 记录历史
     *
     * @param action
     * @param requestString
     * @throws IOException
     */
    @POST
    @Path("/history/{action}")
    @Consumes(MediaType.APPLICATION_JSON)
    public void recordHistory(@PathParam("action") String action, String requestString) throws IOException {
        ActionDesc apiInfo = apiRegistry.getAllApiDesc().get(action);
        if (null == apiInfo) {
            throw new ActionNotFoundException(Messages.getMessage("worker.action.NotFound", action));
        }

        String date = dateFormat.format(new Date());

        ActionHistory history = new ActionHistory();
        history.setAction(action);
        history.setRequest(requestString);
        history.setDate(date);
        history.setLabel(apiInfo.getComment());
        apiHistoryService.recordHistory(history);
    }

    /**
     * 执行请求Action
     *
     * @param requestString
     * @return
     * @throws IOException
     */
    @POST
    @Path("/{action}")
    @Consumes(MediaType.APPLICATION_JSON)
    public void executeAction(@Suspended final AsyncResponse asyncResponse, @PathParam("action") String action, String requestString) {
        Map<String, String> requestHeaders = new HashMap<>();
        if (null != request) {
            // 设置请求头信息
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                requestHeaders.put(headerName, request.getHeader(headerName));
            }
        }
        apiService.execute(action, requestString, requestHeaders, asyncResponse);
    }


}
